#include "Tcpserver.hpp"
#include "Http_protocol.hpp"

// Http协议（无连接，无状态）

string Suffixtotype(const string &suffix) // 通过后缀确定文件类型
{
    if (suffix == ".html" || suffix == ".htm")
        return "text/html;";
    else if (suffix == ".png")
        return "application/x-png;";
    else if (suffix == ".jpg")
        return "image/jpeg;";
    else
        return "no-entering;";
}
string Codetodesc(int code) // 状态码转换成对应的状态码描述
{
    switch (code)
    {
    case 200:
        return "OK";
    case 301:
        return "Move permanently";
    case 302:
        return "Redirect";
    case 307:
        return "Temporary Redirect";
    case 403:
        return "Forbidden";
    case 404:
        return "Not Found";
    case 504:
        return "Bad Gateway";
    default:
        return "unknow";
    }
}

string deal(string s) // 服务端处理客户端请求信息
{
    // ！！！对收到的客户端数据信息做解析并打包返回

    HttpRequest req;
    // 1.反序列化(就是隔离请求行，请求报头，有效载荷)
    req.Deserialize(s);

    // 2.解析请求行内容并提取url路径
    req.parse();
    // cout << req.Url() << endl;

    // 3.在URL目录下读取数据客户端访问想要的信息
    string content = req.Get_filecontent();

    // 4.构建响应
    HttpRsponse rsp;

    if (content.empty()) // 如果读取的文件数据为空
    {
        content = req.Get_404();
        rsp.Set_statuscode(404);
        rsp.Set_statuscode_desc(Codetodesc(404));
    }
    string httpheader = "Content-Type: " + Suffixtotype(req.Suffix());
    httpheader += string("charset=utf-8") + "\r\n";
    httpheader += "Content-Length: " + to_string(content.size()) + "\r\n";
    httpheader += string("Location: https://www.baidu.com/") + "\r\n";
    // 当识别到状态码为307，则表明是临时重定向到location下的网址，浏览器此时提取location字段后发生二次请求

    // 307一般用来临时页面跳转，例如软件界面的广告
    // 301是永久性重定向移动，请求资源已被永久的移动到新的url，浏览器会自动定向到新的url

    // a.http无状态-cookie(用来保存客户的登录信息,登陆之后，浏览器自动填充cookie)
    // b.还有一种方式是将session（保存客户端登录信息）与sessionid（随机数）构成映射，所以就直接返回sessionid给客户端

    // string cookie_name = "Set-Cookie: username=chenrui\r\n";
    // string cookie_passeord = "Set-Cookie: password=666666\r\n";
    // string cookie_status = "Set-Cookie: status=login\r\n";

    // httpheader += cookie_name + cookie_passeord + cookie_status;

    string rsp_content = rsp.Serialize(httpheader, content);

    req.my_debug(pthread_self()); // 打印客户端信息

    return rsp_content;
}

void daemon(bool is_change)
{
    // 一个会话内部可以有多个进程组，但默认任何时刻只有一个进程组在前台

    // 1.守护进程自己是一个独立的会话，不隶属于任何一个bash会话。

    pid_t fi = fork(); // 当父进程退出时，进程组ID不会改变，仍然是原来的父进程ID

    // 2.让自己不要等于组ID，关闭父进程，守护进程也就是孤儿进程,其父进程是系统（pid=1）
    if (fi > 0)
        exit(0);

    // 3.返回新的会话，即pid=pgid=sid（条件是，调用setsid的进程pid不等于进程组ID）
    pid_t si = setsid();
    if (si == -1)
    {
        cout << "调用该函数失败失败,不能是组长调用该进程" << endl;
        exit(-1);
    }

    // 4.是否将当前工作目录更改为根目录（以便以绝对路径下查找任何文件）
    if (is_change)
        chdir("/");

    // 5.守护进程不需要进行输入输出到终端中,将输入输出重定向到/dev/null（自动丢弃）
    int fd = open("/dev/null", O_RDWR);
    if (fd > 0)
    {
        // 重定向
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
}

int main(int argc, char *argv[])
{
    daemon(true);

    if (argc != 2)
    {
        cout << "格式错误\n正确格式:" << argv[0] << " port"
             << endl;
    }
    uint16_t port = atoi(argv[1]);

    unique_ptr<Tcp_server> tsv = make_unique<Tcp_server>(port, deal); // 创建套接字，且绑定并监听

    tsv->loop(); // accept等待客户端套接字连接
}
